{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(160, 2) (160,) (40, 2) (40,)\n"
     ]
    }
   ],
   "source": [
    "mean1, mean2 = np.array([0, 2]), np.array([2, 0])\n",
    "covar = np.array([[1.5, 1.0], [1.0, 1.5]])\n",
    "X1 = np.random.multivariate_normal(mean1, covar, 100)\n",
    "y1 = np.ones(X1.shape[0])\n",
    "X2 = np.random.multivariate_normal(mean2, covar, 100)\n",
    "y2 = -1 * np.ones(X2.shape[0])\n",
    "X_train = np.vstack((X1[:80], X2[:80]))\n",
    "y_train = np.hstack((y1[:80], y2[:80]))\n",
    "X_test = np.vstack((X1[80:], X2[80:]))\n",
    "y_test = np.hstack((y1[80:], y2[80:]))\n",
    "print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 设置颜色参数\n",
    "colors = {1:'r', -1:'g'}\n",
    "# 绘制二分类数据集的散点图\n",
    "plt.scatter(X_train[:,0], X_train[:,1], marker='o', c=pd.Series(y_train).map(colors))\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "### 定义一个线性核函数\n",
    "def linear_kernel(x1, x2):\n",
    "    '''\n",
    "    输入:\n",
    "    x1: 向量1\n",
    "    x2: 向量2\n",
    "    输出:\n",
    "    np.dot(x1, x2): 两个向量的点乘\n",
    "    '''\n",
    "    return np.dot(x1, x2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from cvxopt import matrix, solvers\n",
    "\n",
    "### 定义近似线性可分支持向量机\n",
    "### 软间隔最大化策略\n",
    "class Soft_Margin_SVM:\n",
    "    ### 定义基本参数\n",
    "    def __init__(self, kernel=linear_kernel, C=None):\n",
    "        # 软间隔svm核函数，默认为线性核函数\n",
    "        self.kernel = kernel\n",
    "        # 惩罚参数\n",
    "        self.C = C\n",
    "        if self.C is not None: \n",
    "            self.C = float(self.C)\n",
    "    \n",
    "    ### 定义线性支持向量机拟合方法\n",
    "    def fit(self, X, y):\n",
    "        # 训练样本数和特征数\n",
    "        m, n = X.shape\n",
    "        \n",
    "        # 基于线性核计算Gram矩阵\n",
    "        K = self._gram_matrix(X)\n",
    "                \n",
    "        # 初始化二次规划相关变量：P/q/G/h\n",
    "        P = matrix(np.outer(y,y) * K)\n",
    "        q = matrix(np.ones(m) * -1)\n",
    "        A = matrix(y, (1, m))\n",
    "        b = matrix(0.0)\n",
    "        \n",
    "        # 未设置惩罚参数时的G和h矩阵\n",
    "        if self.C is None:\n",
    "            G = matrix(np.diag(np.ones(m) * -1))\n",
    "            h = matrix(np.zeros(m))\n",
    "        # 设置惩罚参数时的G和h矩阵\n",
    "        else:\n",
    "            tmp1 = np.diag(np.ones(m) * -1)\n",
    "            tmp2 = np.identity(m)\n",
    "            G = matrix(np.vstack((tmp1, tmp2)))\n",
    "            tmp1 = np.zeros(m)\n",
    "            tmp2 = np.ones(m) * self.C\n",
    "            h = matrix(np.hstack((tmp1, tmp2)))\n",
    "\n",
    "        # 构建二次规划求解\n",
    "        sol = solvers.qp(P, q, G, h, A, b)\n",
    "        # 拉格朗日乘子\n",
    "        a = np.ravel(sol['x'])\n",
    "\n",
    "        # 寻找支持向量\n",
    "        spv = a > 1e-5\n",
    "        ix = np.arange(len(a))[spv]\n",
    "        self.a = a[spv]\n",
    "        self.spv = X[spv]\n",
    "        self.spv_y = y[spv]\n",
    "        print('{0} support vectors out of {1} points'.format(len(self.a), m))\n",
    "\n",
    "        # 截距向量\n",
    "        self.b = 0\n",
    "        for i in range(len(self.a)):\n",
    "            self.b += self.spv_y[i]\n",
    "            self.b -= np.sum(self.a * self.spv_y * K[ix[i], spv])\n",
    "        self.b /= len(self.a)\n",
    "\n",
    "        # 权重向量\n",
    "        self.w = np.zeros(n,)\n",
    "        for i in range(len(self.a)):\n",
    "            self.w += self.a[i] * self.spv_y[i] * self.spv[i]\n",
    "\n",
    "    ### 定义Gram矩阵计算函数\n",
    "    def _gram_matrix(self, X):\n",
    "        m, n = X.shape\n",
    "        K = np.zeros((m, m))\n",
    "        # 遍历计算Gram矩阵\n",
    "        for i in range(m):\n",
    "            for j in range(m):\n",
    "                K[i,j] = self.kernel(X[i], X[j])\n",
    "        return K\n",
    "    \n",
    "    ### 定义映射函数\n",
    "    def project(self, X):\n",
    "        if self.w is not None:\n",
    "            return np.dot(X, self.w) + self.b\n",
    "    \n",
    "    ### 定义模型预测函数\n",
    "    def predict(self, X):\n",
    "        return np.sign(np.dot(self.w, X.T) + self.b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "     pcost       dcost       gap    pres   dres\n",
      " 0: -1.6893e+01 -2.5699e+01  7e+02  2e+01  7e-15\n",
      " 1: -3.2732e+00 -2.3311e+01  6e+01  1e+00  5e-15\n",
      " 2: -1.7627e+00 -9.1626e+00  1e+01  1e-01  1e-15\n",
      " 3: -1.6913e+00 -2.8410e+00  1e+00  1e-02  2e-15\n",
      " 4: -1.8579e+00 -2.3123e+00  5e-01  4e-03  1e-15\n",
      " 5: -1.9334e+00 -2.1193e+00  2e-01  1e-03  8e-16\n",
      " 6: -1.9742e+00 -2.0305e+00  6e-02  4e-04  1e-15\n",
      " 7: -1.9872e+00 -2.0040e+00  2e-02  8e-05  9e-16\n",
      " 8: -1.9936e+00 -1.9941e+00  5e-04  1e-06  1e-15\n",
      " 9: -1.9938e+00 -1.9938e+00  2e-05  5e-08  1e-15\n",
      "10: -1.9938e+00 -1.9938e+00  2e-07  5e-10  1e-15\n",
      "Optimal solution found.\n",
      "29 support vectors out of 160 points\n",
      "Accuracy of soft margin svm based on cvxopt:  0.925\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "soft_margin_svm = Soft_Margin_SVM(C=0.1)\n",
    "soft_margin_svm.fit(X_train, y_train)\n",
    "y_pred = soft_margin_svm.predict(X_test)\n",
    "# 计算测试集准确率\n",
    "print('Accuracy of soft margin svm based on cvxopt: ', \n",
    "      accuracy_score(y_test, y_pred))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXYAAAD8CAYAAABjAo9vAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzsnXlcVHX3xz8XRGUTFZdcYDDTyj1x33E3V9IyGxE1xaXSntLMfB6XflG5PWpqKu7KpOQCaG6YSk9uuYKaayogbiAIyA4z5/fHV5Bl7sydYZYLfN+v17xqZu5y7qXOPXO+53yOQETgcDgcTtnBxtoGcDgcDse0cMfO4XA4ZQzu2DkcDqeMwR07h8PhlDG4Y+dwOJwyBnfsHA6HU8bgjp3D4XDKGNyxczgcThmDO3YOh8MpY1Swxklr1KhBHh4e1jg1h8PhlFouXrz4jIhq6tvOKo7dw8MDFy5csMapORwOp9QiCEK0lO14KobD4XDKGNyxczgcThmDO3YOh8MpY1glx66NnJwcxMbGIjMz09qmcCRQuXJl1K9fH3Z2dtY2hcPhFEE2jj02NhbOzs7w8PCAIAjWNoejAyJCQkICYmNj0aBBA2ubw+FwiiCbVExmZiZcXV25Uy8FCIIAV1dX/uuKw5EpsnHsALhTL0Xwv5UVUakADw/Axob9U6WytkUcmSGbVIwh3LkDrFsHXLrE/tvu1Anw8wPq17e2ZRyOmVGp2H/s6ensfXQ0ew8ASqX17OLICllF7PogAmbNAjp3BipUAGbPBmbOBJKSgJYtgaVLrW2haVi+fDnS8/7HNQMRERE4ePCg2Y7PMSNz5rxy6nmkp7PPOZyXlKqI/f/+DzhxArh5E6he/dXn/foxB9+7N+DiAkyYYD0bS4parcby5csxevRoODg4mOUcERERuHDhAt59913J++Tm5qJChVL1n0vZJCbGsM855ZJSE7EnJwPLlgHBwYWdeh5ubkBQEDB/PpCTY/jx09LSMHDgQLRs2RLNmjVDUFAQACZ/8OzZMwDAhQsX0KNHDwDA/Pnz4ePjg549e6JRo0ZYv349ACA8PBzdunWDt7c3mjRpgsmTJ0Oj0QAAduzYgebNm6NZs2aYNWtW/rmdnJwwd+5ctG/fHv7+/nj06BG8vLzg5eVVyMZDhw7hgw8+yH8fHh6OwYMHAwDCwsLQsWNHtG7dGu+//z5SU1MBAOfPn0enTp3QsmVLtGvXDsnJyZg7dy6CgoLQqlUrBAUFITExEcOGDUOLFi3QoUMHXLlyJf8a/fz80LdvX4wZM8bwm8oxPe7uhn3OKZ8QkcVfnp6eVJTr168X+6wga9cSvf++zk2IiKhzZ6J9+/RvV5Tdu3fThAkT8t8nJSUREZFCoaD4+HgiIjp//jx1796diIjmzZtHLVq0oPT0dIqPj6f69evTw4cP6cSJE1SpUiW6e/cu5ebmUu/evWnXrl308OFDcnNzo7i4OMrJySEvLy8KDg4mIiIAFBQUlH/ugucsSE5ODrm5uVFqaioREU2ePJm2b99O8fHx1LVr1/zPf/zxR1qwYAFlZWVRgwYN6Ny5c0RElJycTDk5ObR582b65JNP8o/76aef0vz584mI6NixY9SyZcv8a2zdujWlp6drvWf6/mYcMxAYSOTgQMQyk+zl4MA+z/teoSASBPbPvM85ZQIAF0iCjy01Efu9e0Dr1vq3e+cd4P59w4/fvHlz/P7775g1axb+/PNPuLi46N1n6NChsLe3R40aNeDl5YVz584BANq1a4fXX38dtra2GDVqFE6ePInz58+jR48eqFmzJipUqAClUon//e9/AABbW1sMHz5c7/kqVKiA/v37Y//+/cjNzcWBAwcwdOhQnD17FtevX0fnzp3RqlUrbN26FdHR0bh16xbq1KmDtm3bAgCqVKmiNZ1y8uRJ+Pj4AAB69uyJhIQEJCcnAwCGDBkCe3t7aTeRY36USiAgAFAoAEFg/wwIYJ/nLaxGRzOXn7ewyqtmyh2lJmlqb8/SMfpITgYqVzb8+I0bN8bFixdx8OBBzJ49G3379sXcuXNRoUKF/FRK0brtoiV/ee+1fc4ettqpXLkybG1tJdk5cuRIrF69GtWrV0fbtm3h7OwMIkKfPn2wY8eOQtteuXJFUlmiNtvy9nN0dJRkF8eCKJXaK2B0LazyiplyRamJ2AcOZDl0tVp8m7Q04MABoH9/w4//6NEjODg4YPTo0ZgxYwYuXboEgOXYL168CADYs2dPoX1CQ0ORmZmJhIQEhIeH50fG586dw/3796HRaBAUFIQuXbqgffv2+OOPP/Ds2TOo1Wrs2LED3bt312qLs7MzXrx4ofW7Hj164NKlS1i/fj1GjhwJAOjQoQNOnTqFf/75BwCQnp6O27dv46233sKjR49w/vx5AMCLFy+Qm5tb7PjdunWD6mVUFx4ejho1aqBKlSqG30SOdeELq5yXlBrH3rYtUKsWsGaN+Db+/kD37satI129ehXt2rVDq1at4O/vj3//+98AgHnz5mH69Ono2rVrsai6Xbt2GDhwIDp06ID//Oc/qFu3LgCgY8eO+Prrr9GsWTM0aNAA3t7eqFOnDn744Qd4eXmhZcuWaN26NYYOHarVFj8/PwwYMKDY4inA0jaDBg3CoUOHMGjQIABAzZo1sWXLFowaNSp/AfTmzZuoWLEigoKC8Nlnn6Fly5bo06cPMjMz4eXlhevXr+cvns6fPx8XLlxAixYt8PXXX2Pr1q2G30CO9eELq5w8pCTiTf0yZvGUiOiff4jq1SOaPZvoyZNXn0dFEU2ZQtS4MdHTpxJWIEzAvHnzaPHixcU+P3HiBA0cONAyRlgZvngqM/QtrHJKPShri6cA0LAhcOYMEB8PvPUW4OnJFktbtwYqVgROn2ZRPYdTLtG1sMopVwikY1HPXLRp04aKjsa7ceMG3n77bcnHSE4Gbt9mkgJvvQXwNT7LY+jfjMPhlAxBEC4SURt928mqKoaIJItLubiwvDvHOlgjIOBwONKQTSqmcuXKSEhI4A6jFEAv9dgrG1NXyuFwzI5sIvb69esjNjYW8fHx1jaFI4G8CUqcMoBKxWrdY2JYBY2/P8/Ll3Jk49jt7Oz4NB4Ox9JwGeAyiWxSMRwOxwpwGeAyiWwidg6HYwVKSbdqbCwQFQU4OAAtWrB5DBxxeMTO4ZRnZN6teuYMMGAA0KoVG7Lj48OmAfr7A9nZ1rZOvnDHzuGUZ/z9WRhcEAcH9rmV+e03YOhQ4P33gQcPgFOngL//ZnpQp04Bw4YZN3uhPMAdO4dTnpFpt2pSEuDrC+zfD4wfD9jvfTXAu+VQD+wfpQJR2RmHaWpk03nK4XA4efz0E3D2LPDLLyheuQMADg64/00AegQoce8eIFH1utQjtfOUR+wcDkd2HDwIfPjhyzcilTsN1s9B5cosPcMpDHfsHA5HdmRkAM7OL9/oqNypUgUoMv+GA+7YORyODHnjDeDlrBvRCh1NfXfcvcuWBTiFMZljFwTBVhCEy4Ig/GaqY3I4nPLJhAnA2rUvq15EKnd+7+mPHj2A2rWtYaG8MWXEPh3ADRMej8PhlFM6dADefhsYNw7I+aB45c6NLwKgPKDEy0FnnCKYxLELglAfwEAAG0xxPA6HU74RBGDHDuDFC6BxY+DHB0ocWReFX3dqMKxVFLqtVWLnTjZkh1McU0XsywF8BUBjouNxOBxtqF7Vc8PDg723Ejk5OThy5AgSExPNcnxHRyA0FPj1V+D+fVazrlIB777L3vfqpXv/W7duYdq0adBoyp9bKrHigiAIgwDEEdFFQRB66NjOD4AfALjLpF2ZwylVyEiJMTY2FiEhIahTpw7s7e3Neq62bQ0bqqPRaLBq1Sp8++23WLBggfkMkzGmiNg7AxgiCEIUgJ0AegqCEFh0IyIKIKI2RNSmZs2aJjgth6MHU0S3MoqQ5aDEqFarcfz4cezcuRNeXl4YPny42R27IcTExKBPnz7YuXMnzpw5g08++QQ2NuWw+E/KxGupLwA9APymbztPT0/Tj+/mcAoSGEjk4EAEvHo5OLDPLXkMUyIIhW3JewmCRU7/9OlTWrt2LalUKkpJSbHIOaWi0Who8+bNVKNGDfr+++8pNzfX2iaZBQAXSIIvNqmkwMtUzAwiGqRrOy4pwDE7Hh4sVVEUhYLpv1rqGKbESvZoNBqcOXMGp0+fRq9evfDOO+9Ink1sCZ4+fQo/Pz9ERUVh27ZtaNmypbVNMhtWkRQgonB9Tp3DsQim0BmXm1a5FZQYExMTsWXLFvzzzz+YOHEiWrduLSunvmfPHrRs2RJNmzbFuXPnyrRTN4RymHzilAtMoTNekmOYIzdvQSVGIsL58+exYcMGNGnSBGPGjEHVqlVNfh5jSUpKgo+PD77++msEBwfj+++/R6VKlaxtlmzgjp1TNtEW3VasCKSmSne2xkbIedUr0dEsC55XvVLwfMY6fqWSpV00GvZPMzj1lJQUqFQqREREYPz48ejQoYOsovSwsDA0b94cLi4uiIiIQMeOHa1tkvyQkog39YsvnnIsQmAgkULBFhddXYns7AxfCC14DIVC2sKpQqF9kVOheHVMOS3KvkSj0VBkZCQtWrSIwsPDSa1WW9Weorx48YImT55Mbm5uFBYWZm1zrAKssXgqFb54WsZRqVgJXkwMS1v4+1t9cINFFx5tbJi7LoogsEhbbouyANLS0nDw4EHEx8fD29sbderUsYodYpw6dQq+vr7o3LkzVqxYIau0kCXheuwc6yAlDWENdC2Emjofri83L7NF2Vu3bmHt2rVwcXGBn5+frJx6VlYWZs2ahREjRmDJkiXYunVruXXqhsAdO8dwdDlCGTTRaEXM2VavbvoHkb7cvEwGSGdlZSE0NBSHDx/GiBEj0LdvX1SoUOJmdJMRERGBtm3b4s6dO4iMjMSwYcOsbVLpQUq+xtQvnmMvxejLD1u5icZgu11ddefDS3I+sdy8DHLs9+7do2XLltG+ffsoMzPTYueVQk5ODn333XdUs2ZN2rp1K2k0GmubVAhrrj1AYo6dO3aOYehbGNT3vTXR5myt9SAyZlHWBGRnZ9PBgwdp6dKldPv2bYuc0xBu3rxJ7du3p969e1NMTIy1zSnG1atXadWqVZSdnW2V83PHzjEP+hyhDKJRrYg5Ujk/iEzMgwcPaOXKlbR7925KT0+3tjmFUKvVtGLFCnJ1daVVq1bJriInLS2Ndu3aRatWraLY2Fir2cEdO8c8SHGEVopGRdH1sDH2QSS3a9RBbm4uHTt2jBYvXkzXrl2ztjnFiI6Opp49e1KHDh1k+Svi9u3btHTpUjp06JDVIvU8uGPnmAe5RuS6kFJXboiTLkX34MmTJ1y4y0gyMzMpNDSUli9fTvfv37e2OUTEHTvHnJSiaJWIdKePzNGAJAPUajX9+eeftGjRIrp06ZLsFiCfPHlCQ4YMoRYtWlBERIS1zSlGVFQULV++nEJDQ2W1uCzVsfMGJU7ZR6whyNUVyMgoXJ7p4KBff0VfA5KVSUxMREhICGxtbTF06FDZ1X3v3bsXU6dOxfjx4zFv3jxZabzk5ubi2LFj+PvvvzFo0CA0btzY2iYVQmqDknyKVjkcc+HvX3jyEPCqzlys5l6XY3d31/6gsPJkMCLCxYsXceLECXTp0kV2Gi/Pnz/HZ599hr/++gvBwcGy03h59OgRgoODUatWLUyePBkORXsRShG8Qak0IqepPqUBMVVEsVmd0dG676kV5HP1kSfcdfnyZYwdOxYdO3aUlVMPCwtDixYtULVqVdkJd6nVaoSHh+OXX35Bt27dMGLEiFLt1AHwHHupoxQt3MkesVy5lHsqlpu38PpDUeEuuS1Apqam0pQpU8jNzY2OHj1qbXOKERcXRwEBARQYGCi7xWVtgOfYyygyFJAqtRQdDl0UQ++ptuNJydkbCRfuMh6NRoOzZ8/i5MmT6NmzJzw9PWX1C0cMqTl27thLGzJfuCt1qFTA6NHavzP0nup76JZA9TIrC9i7F9i1C0hKAt544xY8PH5DmzbN0bNnT1lpvGRlZWHu3LnYtm0bfv75Z3h7e1vbpEI8f/4coaGhICIMHToU1atXt7ZJkuHqjmUVmQhIyQpD1hyKbgswx6sNQ++pPgVJKWJjWq7lyhXgzTeBjRuBYcOy8P77oXBzO4Ljx0fg3//ui2fPLOvUr14F5s4Fpk8Hfvih8GVHRESgTZs2uH37NiIjI2Xl1Onl4vL69evRuHFj+Pr6liqnbhBS8jWmfvEcewngOfbCGHI/xLadMsU091RXfbvUjt0idqjtHWiKSyDt2FFcuEujIfrPf4hatCDKyDD+Fkrl6VOi3r2J6tUjmjWLaNkyok8+IapencjHJ4fmzfuOatSoIUvhrpSUFAoMDKR169bR06dPrW2O0YA3KJVhSluDUB5FJxq5upb8GqQ2CwUGEtnaim9rinuq6yEjRWxM5FoSnN3p4MGD9NtHH1F23bqFbNRoiPr0Idq2zbjbJ5XkZKKmTYnmzCEq2lV/6dJNql69Pbm69qL79+Up3LV48WI6fvy47BaXDYU7do680Ob0TPGrQ4rD1HduUyo5lkRsTORaNACdnTaNNPb2Wu9ZaChR166muwRt/PAD0ahRLy/xSiAplilImC9QtW+rkWMHR1qxYhW1aqWm/fvNa4chyEW4y5RIdex88ZRjGcQWFgtiTGWPlCohfee2REWRlIoZETuz69SBrW1F2MZq+06Bp39FoUMH4OFD85hOBDRowBZvb9ip4LffD+k5r66jsm1lbBi6AbmXlPj1V+DAAfPYYQh37tzB/v370aRJE/Tq1Qt2dnbWNskk8MVTjryQMvZNn+PXhpRmIV3n1tVYZMpGMLEmqYJVMVquJR32ePTJYgix2q+hwuMYbNoE2Nsbb5o+UlJYL1fr1sA3x74p5NQBIFOdiTnH5sDLC7hyxXx2SCErKwv79+/HwYMH8d5776F///5lxqkbAnfsHMsgpcLE1tbw40pxmLrO7eurveRQWxWLjw87h6FOPu8B4ePD3m/fzn4hFDmvZtQo3PzySyRXrQoSBJC7OxY3Xo82y5RId9V+Deq67li6FGjRQro5hmJjA+TmAk+ePEVMkvYHTExyDHJy2LbWIioqCmvXroVGo8HkyZPhkVf1VA7hjp1jGbRF1kVRq407tlLJHKVGo9Vhwt+fOWRtHDyo/XNts1vz0paGzEWVWOaYmJiILVu24Ozrr4Pu34eg0UCIjsZjLyWyswHbhVrunyAg3etdEAHPn+s3xVicnIBatfagSZOWqIIqWrdxd3HHvn1Aly7ms0OM3NxcHDlyBHv27EH//v0xdOhQWQmLWQOeY+dYjrwGHbGUizlz3WKOXawJSawRrCBS7NWzBkD0Srira9euaN++faEOyB49AEdH4OlTIKT+VNTbtxZCAbvSBQf8oQzAyFAloqOBatV0m2MoSUlJmDZtGg4fPgM3t234fOM9TD5YOMfuYOeAFb0C4P+hEtu3W9a5FxTuGjhwYOnXeNEDz7Fz5EdeZD1lSvHvKlY0r4iWoU1IUlJHUtYNdDQtFRXu0qbGGB8P/PgjMGkSIBw4WMipA4ADpWPAn3NQowaQkKDfHEPIE+6qUqUKbt+OQI0aHbHPX4lFXQKgcFFAgACFiwILPAOwYboSgwYBnTub1gYxyqRwlymRUjpj6hcvdyzHBAYSVaxYvKzPxsa89fiGNnbpK5GUOlhDpMwxq04dncJdT54QbdhA9PrrRF9/TZSWRqQRK4cUBKpShSghQbcpL14QRUezmnRdFBTuCgsLy/88I4Pos8+IqlYlGjSIaOJEoh49iGrVIvrxRyJL9SQVFO5K1ncxZQzwOnaOLNGlqGjuCUTGjMDLs7eoU9XV3VrwHFq6WnMqVqQjvr708OHDYrunpBD5+hK5uBB9+CFznLVrs16u5y4KrffthauC+vUTv4wzZ4hGjCBycmJdo05ORAMHEh07VnzbkydPUsOGDWnMmDH0/PlzrcdLSiLasYNo7VqikBAiSw0Y0mg0dPr0aVq0aBGdP3++RN2tBWvxFcsUFHildDT5ccfOkSdiDUWmbhQyNVIeCrokCxQK0ggCJVWtShe+mEUZGTnFdk9PJ+rYkejjj4kSE9lnyclEdesS/fwz0dyGgZRVobjkwGfVA6lAYF0IlYo9GFatehWpp6cTbd5M5OZG9NNP7LPMzEyaNWsWvfbaa7R3796S3i2Tk5iYSJs3b6ZNmzZRgr6fJnoIvBJIDv4OhPnIfzn4O5QK584dO0eeWDNiL4qppRlErk3t5kZr14bQziE+FFuhHqkhULSgoPVegXTjxqvdlywhGjz4ZUqjgG2ZryloSpVAmjiRaIpLIGXUZg+J5GoKmlo1kFas0G7OzZtENWoQXbum/XrjVwRSvXpEmzdfpmbNmtGwYcNkp6Oi0WjowoULtGjRIjp16hSp1eoSH1OxTFHIqee9FMsUJTfYzHDHzpEnYjl2OzvLat6YQ0xNhyTATx2+oOyKhc+XZedAk5wD6cgRIrWaqGFDljYREwP7dVggVanyylSlkujsWXFzpk1j2i5i16txcKBFrUZQpUo1ZSvcpVKpTC7cpc2pYz5ImF/8F6PcUjbcsXPkS2AgSxrnORlXV/M4dV0RuVTxMEMQOeajinUpp5727zJqK6hGDaKrV5lKokaj27br14kaNZJmTv36RLdu6bbtcaXKVLFijMUWPqVy7do1swh3BV4JJGG+ICliN2XKxlQPCKmOnZc7ciw/Q1WpBJ49e+Venj0z/YQhfY1BurTTjUVLE1amrT1ujlmECg+11+5XfhqN4cNZM6qNzctyex22SSmvzyMlBahZ89W+2qidnQUiN2RlSTumucnIyMCePXsQHh6OUaNGwcvLC7bGdCSLMOfYHBCK30ABAvx7+Rfbtqh8QnpOOuYcm2PQOVVXmb5OdHI0CITo5Gj47feD6qr5/j/jjr28I3UAhKHHNPeDQt85tHWOpqezzwGzDCxRf/gh/p4+PV8SQOOmwGSb9Wi3XCkul2Bri7FjgUOHgMqVgchI3bb9/jvTbJFCvXrAzZuv9tVGbh13ODsDcmjUvHPnDtasWQNHR0f4+fmhXr16Jj9HTLL2BxyBoGyulLSt2OdimOoBYQglduyCILgJgnBCEIQbgiD8LQjCdFMYxrEQ+hwgYPiEIlM/KIw5h76IXIp4mAHExcVhw4YNiGzWDDYxMRA0GsSfj8IBFyUcHSEul6BWo04dNu7Ozw9YvFjctux5/li5Epg6VZpNY8cCa9eydOsf/fqh2GRXBwfsescf48aJN+ZaAksKd7m7aH/AKVwUkrcV+1wMUz0gDEJKvkbXC0AdAK1f/rszgNsAmujah+fYZYQ+PfMpU6TXcBOJ54dtbc1efVIoPy51YlEJq2LUajWdPHmSFi1aRJcuXSq0AJmZSeToSBQfr9ueo0eJ2rZl9eFNmxLNnk2UvaWwbSlrA6l/f6aJLjUf/uwZ0WuvPaHWrYdQixYtKOr77wsdM2JmINWqRXT/vsGXbTKioqJo+fLlFBISQpkWKIg3JG9uqhy7KatwYK3FUwChAPro2oY7dhmhywHqmvwjtsioq07dzNUneodrmHiEYEJCAm3cuJE2b94s2swzZgzRwoW67Xn/fVZnTsTGzw0YwLo5p00j8vcn8vFhDUvTphWfXqSL3bt3k6trbXJ2nk1Dh2bSoUNsMfX4caLRo1l9+6lTJb8PxpCTk0OHDx+mJUuW0K38FV7LYMhCpikWPU25CGsVxw7AA0AMgCq6tuOOXUbocoC6as7Fmol07SPlwSAFQ8bhmWGEoEajoXPnztHChQvpzJkzOssEIyOJatYk2rWL6PCYQEqqymrQNe7Mnq1bWaNQ0c74GzeIvv+eyQn89BNRXJx0+54/f06jR4+mN954g06fPk0pKURr1hB16sQqZerWJercmR37zh3j7kFJePjwIa1atYp+/fVXSktLs7wBVsDSVTGmdOpOAC4CeE/kez8AFwBccHd3N+qiOGZCzAHqir7FHLMUjRVdDwap9lppoHdycjJt376dAgICKE6Ct42JIXrnHXa5TZuy2vN27ViFZ7t2zKn//bfp7Dty5AjVr1+fPvnkE0pNTc3/PC6OzUatV+/Vw+Lzz1kD0wcfMB0Zc5Obm0snTpygRYsW0ZUrV2RXN18asKhjB2AH4AiAL6RszyP2UoJYZCwIup1owQeFrgHSJcHCA701Gg1FRkbmC3dJ6YB89IiZ9v33RLdvs2ahN98keu01orffZs594ULT2JeamkpTp04lNzc3Onr0aKHvUlKIWrQgmjWreConPZ1o7FgiLy/D0jxERFeuEC1fTrRoEdFvvxHpKjePi4ujdevWlUvhLlNiMccOQACwDcByqftwx15K0BYZCwJbUC3JMUwVXVvIuaemplJQUBCtXr2aHj16JHm/8eOZMyUirbbev8+UEp88KZl9p06dojfeeIN8fHy05vqXLiUaPpwtumpLCcTHs6anjz8mCg7WL+p18yZRly4s+p86leiLL4jatydydycKCiq8rSmFuziWdexdABCAKwAiXr7e1bUPd+ylCDHnaYhTNYcDtlA65ubNm7RkyRI6cuQI5eQUF+4SIzGROe2nT3XbOmECi+iNQapwV6NGRKdPa1/EqzDXgezbB1Lbtiz37uXFFm4XLdJefXPnDvvFsXo1UdHbceYMSy1t3Zp3D0wn3KUNubX7WwKrVcVIeXHHXsqxRo676MOhoCSBKVM8L8nMzKSQkBBasWIFRUVFGbz/H3+wBUoi0rnYu3cvE/4ylMuXL1Pz5s31CndlZjIZHo1GvOyu/hIFPX3KbikRq5xp25bl4IsydChz+kTaHeu1a0TVqmno5EnTCncVpTQrNJYEqY6dd55yDEdKU5Mp0daQJDYuyEhJgOxsYONGoFUroFmz+5g9ew127bJBVtYkODsrDD4eUYGmHxNJBABsvqe/vz/69u2LGTNmYO/evahVq5bo9jY2bPKfRiPeEBObGo3W2z2Q8KkNPJZ74HyWCmFhQHAw8Ndfr7Z78AD43//YACyxNvmzaRsxbtwOhIdfgK+vLzp16gQbM0y4tkaQC2GXAAAgAElEQVQ3Z2mCO3ZzY2kdFktgDp0VXWh7kIhhhCTAlSvA57VU6DNRgcuRNjgZ64nO0WqcPTsYCxdWwltvAWfOGHbMpk2Ba9eAxEQdNrm7IywM8PSUdsxbt26hS5cuCA8Px8WLFzFmzJhio/SKYmcHtGnDZnaLdUwKEPAwNRoQXjnoAw9U+PRT4OefX2138SKbZ+rkJO5YZxyegXr16uCffybofOCUFKt0c5YiuGM3J5Zor7cGZtBZyUfbg1DqA8MISYDYWODnLiosTZkId4qBAELV5OcYfngu7ixQYcoU5siGDTPsuVWjBjBwILBwIXCoiz+yKxSXCHg+0x87dgATJ2o/xrNnwKJFQJs2GtSsuRLNmnVGgwY+2LPnCNzc3CTbMnUq8MMPwILu/nCwK2yHAKGYKFZ6TjpmHZmDwYOBU6defV7wV4iYA02mZFSt6gXAdMJd2jBVu39ZhTt2c2JoyqK0RPcm1lnJR9uDcPx4cSETV1c2pFoQ2D8DAvSrRBa5x/+btB3fab6APWUU3i49HcLo0fh2mwcm2KvwzjuFo1d9qNWAoyPTfvn2rhJH3w9AkosCGgh4XFGBa9MD0G2tEl98wcS6ivLHH0CTJsC5czHQaPqgXr1fsHr1GaSnf4IWLWxeiXtJQKkEatUCds9V4oeOAajvpACIDaIu6tTzeJgag0mTgMzMV5+98w5z9Onpuh3rkSPShcqMxb9X8YeUg51DMYXGcouURLypX6Vu8dTYqg4pre8Fz2GlphujMEeli9SuVWPvjbZhEy9fus6VU8mBvvEIpNq1pZ9q8mSi7t1ZpUj79uzSZswgmjePqFkzdtvmztVeeXL7NpGrq4ZmztxMNWrUoB9++KGQJvnGjex4SUnS7cnOJvrqK6b53rcvuw2enkQ2X2hfUHX7r4I+/JCoUqUCE5iI6N13iVasINpycQtVWlCp2OLlkiOBVLWqYbYZC6+KEX8JZMjKjYlo06YNXbhwweLnNYq8KLJg5O3gIC069PBgUWdRFAogKsr4bcsqUlcSbW2BrVsN13AXu8cSiLVVQEFRyMoCKlTQve2VK8CAAUwy19kZgEqFrBlzUPFJDJJc3HF1pD9CnZRITwfWrCm+/7hxT3H6tB8qV47Ch/4fYt0/6xCTHAN3F3f49/KHsrkSH3wAdO0KfPaZYdeRmgocOcL035OTgShnFR618UM2Ff5lWb2yKxz+WIH331Li5Eng3Dn2+fXrwEcfReP990OQUDcae5L2ICYlBraCLdSkRoVUBXzq+mPTv0ysr88BAAiCcJGI2ujdUIr3N/WrVEXsJZm0Y0gUbkh0X1aRGrEbe0+kCJSJvNQQyMFBmrLi1KlECxa8fCPy30DiShbZpqQU3vfXX3eTINSmTz6ZTVsubhEt6Tt2jKh1a+NuAxHR8+dEDRowcbHNFwLJdaFrsajddq4Dbb0USB4eROfOvRLu+vHHpdS//y164w2iQbMDyW6evKYMlWXAI3YTIRZFCgKrIdOHSsVy6jExbHHR3197pMkjdu2/jrRR9J5IuMdEhOy6dVHpyROjTIsRFPjmoygEBurftnNn4McfWUSt6+/a3DkKKhXQogXw/PlzTJs2DWfO/IXHj7ciLa0jPJZ7IDq5+L4KFwXOjIxCq1bA06dGXQ4AYO5cYMsWth6QOsEDKTbaz+UdHYXatR+hSpUQ1KxZEwMHDoS9vQPOngUGhHkgGdr3i/o8Suf5VVdVmHNsDmKSY1DdvjpSslKQo8nJ/97BzgEBgwOKDcAoz0iN2PniqT5KWgGiVDInpNGwf4qlD8y1IGlpSrIArFSyFFfegqirK6vXK0jReyKh8iglJQUqlQqnBg4E2dsbfElZqIg5gj8aNZK2va0tkJv78o2O0tCcHHabwsLC0KJFC7i4uOD8+ctQqzsiNVV3SV9cHFClisGXUojKlYGPPgIOHwZSBPFzOTmFIyvrF3Tt2hUjRoyAg4MDBAHo2BFIgXFlh0Xr4BMyEgo5dYDXpZcE7tj1YSmHW9SpSa3ykBOmKO8s+CB89gzYvFn3PdFReUREuHLlCtatWwc3Nzd0X7cOwvr17DhAsWqbLNghu5KTljoRgpcXc4BS6NIF2L//5RuRACCnjjsSE9OwcuVUTJgwAZs3b8aqVatQrZoj+vcHduzQXXmybRswfLg0e8Ro0oRVuTRvDiiqaj9XNZtqSEp6iLff9kPz5s2L1c0bW3aorQ5eG7wu3Ti4Y9eHJR2u1OherpijI1XfPRGJiCkmBrt27cLJkyehVCrRvXt3NhQ573hEbAVRwUoQU6opMNN1M55ku6JocWUl5GDs7Tm4dEna2u6kScC2baxGXltgQA4OWOrqi5yclsjMTMOVK1fQu3fv/O+nTwe++w6Y0Vp7Sd+kN/yxdSs7T0kYOBC4dw+4cEF7+aAd7DC67ufYu/cjeHtr/3lgbNmhVIdtI9iYdehzmUVKIt7Ur1K1eMqRjjUWgEUWXJOqVpUs3GVvz/TINRoijcg1aASBKlaUPpZuyRImvnXhAhUqDc2t707fKAZRhQqvkUoVLLr/woVsl0mrAsntv2xBsf5SBfksYuPsdu2SZoc+VCqmynj9Olu8rL+0PmE+yPX/XOnb4HXk7q6/qtSYRU8x3Rptr/KgASMVcBGwMoqFdcgNoiQVRMaipeok286O4pYvl3yI7t2Jfv315RuRa0itoaCuXQ0zbeNGpnbYrh3RxIlEffpcJhubZuTmNoxu3RIX7srj8GGifv2InJyYRK6TE9HIkUTnzxtmhz42bWLCXRMmXKRvv11E//3vSXrvPTVVq8auwVCkOPopv02R7NiNnQ9aFpHq2HkqpjQhd4kCc6xH6FuMfZkqy6lbFwQgvWZNYP161Jw+XfIppk5lHaI5OdqvgRwcsNDFH1OmGGb6+PHA/fvAvHm5ePrUH2fP9sHy5TMRHb0XjRvr11Hp14/l9WNjmVbNkyfAzp1M+8WUjBjxAsuX74CHx3k8fOiLa9c6o08fm/zGX0MQEwcrmk45eOegQcfluXbD4OWOpYnSUBIptbxT6rH0NIfl5OTg999/x40bNzB48GA0klq6UgC1GvD2ZtUsAQFAzbBX16Cu546fXvPHsdpKhITob04qyq1bt+Dr6wtnZ2ds2rRJVOMlNRX49Vfg7l3A3h7o39/0Dlwb165dw+HDh+Hp6Ylu3bqxdYgSIFai6WrvimdfPct/b7PARlTOQBtSyifLA7zcsSxiaVVFYzDlArCexdjY2FisW7cO6enpmDJlilFOHWAOfdcuoH59oHFjYOQ+Jb76IAoffqBBjdQo3G2vxJ49hjl1jUaDlStXonPnzvDx8cGRI9qFu4iYUJi7O7BvHytBTEkBRoxg5YT//GPUJemwi50zPT0du3fvxh9//IFRo0bBy8urxE4dEI+sEzISCkXtuqpmhCLL11wDxnB4xF6asGbEbspIXCoizWEkCDjx+++4dOkSBgwYgKZNm5rslImJTIc8Pp4pNHp7s3J6Q4iJicG4ceOQlpaGbdu2oXHjxqLbzpvHHHpwMPvz5qFWA2vXMlXG06dLJpwZHw+sWwds2MD+fG+/fQfe3vvh4dEEvr69YFe0V8AI8pqNtEXreRSMuvNSNmIlj3mqkwoXRb6MAkd6xG7gD0uOVfH3156aMHcTU9GUSF5uHzCvc3d31/ogS61WDU+fPsXkyZPh5ORk0lNWrw58/LFx+xIRtm7dipkzZ+KLL77AzJkzUUFHmB8VBaxaBdy4wdQXC3Zi5unCjH+qxLx5rJzfGG7eBPr2Za9du7Lw+HEY7t69Bxub9/Dddx54+JA9XEqCPiedR8FoPs9Riz0M8pw6T78YB4/YSxvWiJyt9UtBS449x84OD+fNg+Kbb/QOmbAkT58+hZ+fH+7fv4/t27ejZcuWevfJyzQtW6bdOTrYOWBpjwDMHqjEvXtAtWqG2ZSdzZqQZs8GeveOxr+D/o3QrFC80LwAwIS+Kh5fgeXjlRg50rBjF0Qsr14UMUctlm8XIEAzT4JsRzmC59jLKtZoYrJWbv9lxYu6fn0QgFRXV2SvXg2POXNk5dT37t2Lli1bokmTJjh//rwkpw6wiUR5fUliE4l+PDcHb73FVBWlkJHB9GMyM1l6R6HIhZtbGGbvmI2dmTvznToAJGYm4Fnn8Zj9iwqqqyp4LPeAzQI2Hs+QpiApFSu68uR8aIbp4Y6dox9zTkzSARHhwptv4r/TpuHs6dNwjI+Ho9i4ISuQlJQEHx8fzJo1C3v37sUPP/yASpUqSd4/bx4poFsXRqNh2+rir7+AkSPZukDz5mxd4IcfHqFr13VITk7GyUonkUu5xfbLpWxEvT0dE0ILlyj67PWBsECQ5OT1OWBbwRa+LX1F8+TWGJpRkgdZaYA7do5+rCBQlifcdfnyZYwdOxYdO3aUVZQeFhaG5s2bw8XFBREREejUqZPBx+jQgc0iBcSdY10nd9y+zWaoirFtGxvd17Ur8OgR8PixGnv2hKN//19w+HA3ZGSMQGxKrOj+VDkBmerCvxbyUiNidegF0eaYC6ImNbZGbhU9hrK5EgGDA6BwUUAAm+xkTlVHXbX2ZcXh8xw7RxpFc/vvvsu8kolz/USUX1vdrl07dOnSxSRleKYiLS0NM2fOxG+//YZNmzYV0ngxlMePmcOOiAD+TNaeY++WHABFshJr12o/xtWrLJ3zxx/AxRwVvg77GrGpsXCt4Io3ohZiWMOPsWwZYPulBx5niOTBCSgmkFMEfQuZBRd+bQQbqElt8DEsha5a+4zcjGJ/AzlJB/McO4dhqjmqBXP7777LavFM3AGblpaGXbt24c8//yws3CUTTp8+jZYtWyItrbhwlzHUqQP85z/MMTdVF45a3aoo0D8nALd3K7FggfgxVq4Epk0DLmar8HHIx4hNZZF5Qm4CItymYdMFFT77DHj7kT/sbIqXNVYQKsI2R389p748urK5ElGfR0EzTwMNaV/wtEb3qLYIXFetvbZ1jtIoHcwj9rJMScb66Tqmj492mcMSVMncunULv/32G5o3b46ePXvqLBO0NFlZWZg/fz62bNmCNWvWYNiwYSY9/saNbOjF668Dnp7Aixestr19e/anqltXfN/q1YFTp56j655GSFAnFPu+QpoCc52jsHw58FO4CtMPTUdCBtuuWiVX2B5dgdFKIOCJ7nJFQ6JtXQNCLBmxi1Ua2Vewz78HUpBTdQ6P2DnmkdGdM0dcu9aIKpnMzEyEhobi8OHDGDFiBPr27Ssrpx4REYE2bdrg5s2biIyMNLlTB1jdfFQUMGMGy2p5egJnzwK//abbqRMRXn/9Evbv34BEdaLWbdSOMdiyBXj+HFAkKxE19RliJxAWORDsVz7DV/2VWDb+1a8FoOSdn/oWQy2VxxarNMqzp6h9rvbaf7mUxuocHrGXZUo61s+QYwIGR+z3799HaGgoGjZsiL59+xpUUWJucnNzsXDhQixfvhxLly6Fj4+PwYu3WVmsk9XJ6eVQaxPy4sUL7N+/H2fOvMCoUd746Fw70Sh5Z4coDBgA1KvH9NcdHJgW+yefAO3aFT+2tkYpQ3PMYscQi6LNkcfWVR+//b3txewDYDHbjEVqxM4de1nGHI1FYscUBDa4QkKKxxTCXTdvAlu3sh8Jzs7A0KFMDVFfWaAU8oS7nJycsGnTJrgbWNZ54wawdCkT9bK3ZwJfnTqxARqDBpXcvoLCXb//3g0vXtii/URxh/nXeiVcXID/+7+Sn7uk1FhUQ2saxBxpGmNSQqZ4qJkTnorhmKdMUdsxBQGYPFmSUy+pcFdGBjBqFNC9O/vh0L8/8NZbLEPUpAmrEjlwgMnNjhgBfP45qzqRgkajwapVq/KFu8LCwgx26seOAd26AQ0aAHfusGahxERg7FjgX/8qWfu+NuGuqVNtERQEVI3RXjLoEq3Er7+yP4+1UV1Viea2zbGwakx9fMFF4KjPo2Tl1A1Cimi7qV980IYFMcdgDiOOmZubS8eOHaPFixfTtWvXjDqtRkM0eDAbNpGRUfy7778nsrUlatWKaNUqoqAgogUL2LCLQYOIkpPFjx0dHU09e/akDh060K1bt4yyLz6eqEYNovBw9r7owIk1JwOpUSOi0FDDj3379m1aunQpHTp0iLKzswt9d+YMUe3aRGPGEP3vf0RRUeyfXaYEks2Xhk020oUxk5IKomtqUt7xSnJ8c9gsNyBx0AZPxRiKNbRaSjlxcXEIDg6Gs7MzBg8eDGcjE84nTrC8cGQkYGeHQn+L3LrumJ7mj/sdlWjYkJUB5pGbyyLWe/eAo0eZTG8eZKBwly4WL2at/5s3i1dkjK8ZgL93KHH8uLRjZmVlISwsDPfu3cPQoUPhUVACsgAJCey8v/zC1BxtW6nwqI0fcqA/Xywl/aC6qsK4kHHI0eTkf2ZnY4fNwzZLjmp1abBPaTMFWyO3yjq/LQd4jt0cGFs+WE4fBhqNBmfOnMHp06fRu3dvtGrVqkTdox9+yLorP/kEWv8W2RUc8OK/AXBXAa4j5yA25ZWj+rCJEu3aAQsWvMpz5wl3RUVFYdu2bZI1XsRo355pq/foIZ7fda+iQNL8KNy7p18OODo6GiEhIWjQoAH69etn0OKy1Pyy1MVMsdx40QEaxtjkau8Kp4pOsiiRlDs8x24OjCkflPs4OzORmJiILVu24M6dO5gwYQKI3sFXXwkYPx6YNYtF3YZy4wZbhASg9W9RMTcdh/dOR0YfPzxIKdwuvvM6a9RZt45tmyfc1bRpU5w7d67ETh0AkpOZ/C4gnjN+kBKDatXYMA0xcnNzceTIEezZswcDBgzAkCFDDK4Y0qU9UxCxksCiTTliuXFD6sHFct4rBqyQbC9HGtyxG4IxKofmqCWXMUSE8+fPY8OGDXj77bfx7ru+GDGiGry92Y+bzp2BihVZuV2/fmxhUSoVKzLVQgCi9/ybVgmgCtodVYcOwPXrz/OFu4KDg/H999+brMyyXj3g1i3272K1z/Wc3ZGYyMS6tPHo0SOsW7cOKSkpmDx5ss4hHbrQp5iYV0suJrdrDoeqSxOGKzyaFu7YDcEYlcPSMM7OROQJd0VERGDcuHFo0aIj+vYV0LYty28vWMCacf7v/1i1ZZMmzLlnZEg7vpcXsGfPyzci9/yBi/Z9Y5JjcOxYGGJiWqBKlSqIiIhAx44dDb5GXfj6vvpFIBadeqn9MXBg8bp2tVqN8PBwqFQqdOvWDSNGjIBD0eojA9BVEVJQBEuMog5VrHlH7HMxxKpOrKHwWJbhjt0QjCkftJLkrSUhIly5cgXr1q2Dm5sbxo8fj5o1a2LLFlYy/8MPgO3Owpo1FYJU+O9/WUv8L79IO8/kycCWLS/L6LX8LdSVHOCSrt3ROKod8c03EzBkyGasXr0ajo6OJblkrXzwAXuA/fST9uh0dtMAHF6sxIwZhfeLj4/Hxo0b8fDhQ0yaNAnNmzcvsZKlruhYW/qlINoc6ooBK1DRtmKhzyraVsSKAStKZKcUezlGIKV0Rt8LQH8AtwD8A+BrfduX6nJHQ0v9AgOJHByIWIadvRwcTFN2KANSU1MpKCiIVq9eTY8ePSr0XbNmRCdOkM57cOgQUZs20s/3009EHh5Ex44RaQr8LZKqKmiiYyA1+TCQHL5zKFRKJ/xboLbju1D16s/p5k1TXn1x7t4leuMNooEDiX77jb3/6y+iqVOJXF2JQkJebavRaOj06dO0aNEiOn/+PGk0GvMa9xJhvqC37FAbpiwdLGtliJYCEssdTeHUbQHcBfA6gIoAIgE00bVPqXbsxiD2MDBHjbkFuXnzJi1ZsoSOHDlCOTk5hb7TaIhsbIhycohdW0GnnvdSKCg1lcje3rDzBgURNWlC9OabRN7eRN26EVWvTtSoEVHv3kSLDgWS+3/dCfNANl/akPfcz6lBA6Jly0x26TpJSyPauJGoc2f2EHJzI+rQgWjiRKJffiHKzCRKTEykzZs306ZNmyghIcEsdog5T7F6csUyhVns0GaXg3/hh6+Dv4NO527og6CsPjikOvYSlzsKgtARwHwi6vfy/eyXvwR+ENun1JY7mhJzKC9aiKysLBw+fBhRUVEYNmwYFApFsW2IgEqVmPiUo7O4Zk1CvAYNGhSuEtFogOPH2S2Kj2dlgR9+WFgygAg4fx548ABwdGTdnhUrsrTPihWRSE/3QZUqDWFvvw7OzrXwn/8A779vphsiwr59LH305pvAgAGsQTcsjJCTcwm9ex9Hz56d0aFDB9iYQgehCLrKGAHTaKIY235vaKu/ofoyltSjsTSWLHesB+BBgfexLz/j6MLS1TIm0mW/f/8+1qxZAxsbG0yePBkKhQJEQHg4MHEim+Tz8cfA77+ztv+QEOhcZwgOZnXfeTx8CLRtC3z5JdC6NTBpEhOqmjMHaNXqlcSNILDPhw9nsgIs3Z4LW9vvYWPTB9Onz0BAwF7s2VMLkZGWd+oHD7Ln9u7drLHqq6+AyZNfYNy4HRg27CJ27vTF48edjHbq+hQSdZUxmiKfLTaFaOqBqXqVGw0tbZRakmns9mURU0Ts7wPoR0QTXr73AdCOiD4rsp0fAD8AcHd394zWJiRVnjCH8qIYJvh1ICbc9fgx4O3NIu4JE9gzIzaWaYynpLCoPXKWCpU+LX7+jJ8C0HqJEsuWMef84gVr8hk9Gpg9m92KPIiA5cuB1atZpF6tWmH7bt++jTFjxsDJyQmbN2+Gm5ubkTer5Gg0QOPGrEKmVy/mBGcemonHGY9Rq1ItLBmwBI0yfDBiBHtQGdroKiUi1aVsWBJt8bwoXayiRoBQ6LzaImVDI3ZDr8Vc1y4HLBmxxwIo+H9RfQCPim5ERAFE1IaI2tSsWdMEpy3lWLJapoS/DsSEu9LSgL59WZrh77+BL74A3nuPTfSJiGAdoo8eAT03KPFwfgArkREEQKHAg7kB6LVRie7dWYoFYGqNb70FfPMN26xgVNpghQdq9VahQwdgw4ZXtmk0GqxcuRKdOnXC6NGjERYWZlWnDjAhMGdnoGdPYPOFzRgfPB6PMx4DAOKy4jD5wGTcdVTBw4NprhuKlIjUHHXhUsokizpUbZGyoaWNhl4Lr4k3jWM/D6CRIAgNBEGoCOBDAPtMcNyyjSUHRBtZS69Wq3H8+HHs3LkTXl5eGD58OOzt7fO/376dqRjOnfsyui6Q7hEaeGBGHRUGDwaqVAE8lyrRpX4URo3UoGOdKLRfoYS3N/Dzz68i8/Xr2UMBEP+p32i4CuvX55kfgz59+mDHjh04c+YMPv30U7PkqwuSnMxuW1qa+DaRkazm/p9/7uDLg18im7ILfZ/n7Ly8xDtwdaVapKQyzFEXrq9MUoyi9hqaCjL0WnhNPFDiUTVElCsIwqcAjoBVyGwior9LbFlZJy8FYgkNGXd37RrqOn4dFBTumjRpklbhroAAYNGiAk69YLrnpXTCglnAgO1KxMSwXPOzZ0DNmszx2RUZwXn7NsuvA+JR6aboOXjwz0fYvHkbvvpqBr788kvMnDnT7LNRDx8Gli0DTp8GXFyYgx80iE098vQsvK2NTRYqVQrDgQN3kURJWo8XkxyD3FyWqipK0VRL3kMNQH6XplYdmgIRaZ6TNKW2uLHdqNoiZWVzpWRbDL0Wc1x7aYOLgJUHDMixFxTu6tWrF9555x3RZhknJ5Zjd3aG6AAOclegQmwUsrMLqypqo1o1NkCjdm3deVLh2yFo1uy+SYS7pLBwIZvd/X//xzTeK1cGkpJY6sjfn+XSvb3ZttHR0QgKCkHovWg8eHMPHqRod4buLgo4rY/CqlXsIVcQfTloa1V96JIgAACnik7QkKZMVqPIBS4CxnmFUsmceIEctzanXlC4a+LEiWjdurXODsiKFQukJMTSOg9iYGsrbbJRv35s6hCgIx+aIuD11982mXCXPo4dA9asYZG60FKFt9ay9EirLR6o0VOFI0dYNdDdu6+Eu4SWOThTc52oU3ewc4C3sz+IClcE5aEv1WKtLk1tKY48HOwcsHbQWt49KhN4xM4BEeHChQsIDw9H165d0b59e0kt7R9+yES9PvsMohF7qqsCH3aIkrRIePIkq4g5fx4Ie1I8KhVyBbj8by6C5sxH374GXGAJGDKEjd2r3E48Sr681QuOjsFo1qwWBg4ciCYBTUQjW3cXBQZV9sev/1Fi3z5Am1yNMSPdLEXBqhhbwRZqUkPhoih3qQ5rwfXYOZJISUnBvn37kJGRgWHDhsGQiqU8R3zhAlDjSPF0D9k7YIZLAHpvUmLAAGnHnDuXCX2tWgU8clXhiwNfIC4rDvY5TnC9vAIjm4zHkiWGXqVxZGWxfHpCAtB0vXZnW7tSbUynL3HiRH8cOdIMgiCID5QgAe6bNahbF1ixQvsgaaBsN9hwSoZUx17ixVNO6YSIcPXqVRw5cgRt27ZF165dDV6A7NKFZXN69AB+/lmJrgGAMGcOKCYG2bXd8W1lf+QOUaJ/f+nH/PZboGFDYOrUNDx6dBpZWZVQ1zUMTk59MGMGq5W3FGlpLJ/u6CieHnma9RTDR03CypVV8qt7xBY3q1dwR0gI8M47us9r7cU/uQ905uiHO/ZySFpaGg4cOIBnz55BqVSibt26Rh/ru++YI/bzA4iU8HhTiVhHFu1++SVrqTdUqLBRo9PIyRmDrl074eOPr0ChqIp33jH8OCWlShXWGPXokbizdndxR0xMFdQr0Gvt38tfa8T902B/vNNc2rkNqRoxJfoqcjilA56KKWfcunULv/32G5o3b46ePXsaPd+zKETApUtAXBzTdmnTRtqCaUGysrIwb948bN26FatXr8Z7771nEttKwpQprEqn0XAVJu6biIzcV+LxeemR4AVKeHm9HNn3EnNHveY6viXz+/yXgeHwVAynEAWFu0aMGKFVuKskCELxem5DiIiIwJgxY9CwYUNERkaiVt6MOSvzrxR20/MAABadSURBVH8BnTsTfnrzbQwVhuJE5ROIy4zLd0QZfylx4QKwaVPh/cwZcZszqrbUiDr+y8C88Ii9HHD//n2EhoaiYcOG6Nu3r8lGwZmC3NxcLFq0CMuXL8eSJUvg4+NT4iETpuTFixfYsmU/bt16gexsbyiVtVCnDnDnDqtfv3qVNS+9+aZpzzv1wFQEXAyAmtSwFWzh5+mHnwf+DMC8UbWlInY5V/7IGR6xc0SFu+TCrVu34OvrCycnJ1y8eNHqGi9F+fvvv3Ho0CF4enrigw9GYutWW3z5Jes6fe01Ngpvxw62uGpKph6YijUX1uS/V5M6//3PA382a1Qttj5g6nZ8PrzavPAGpTKKmHCXHNBoNPjpp5/QuXNn2Qh3FSQjIwN79uxBeHg4Ro0aBS8vL9SubYuvvmKlnXfuAH/+ySp0zDBhDwEXA3R+bm6RK/sKr/SAXO1dzVJmyYW6zAt37GUMfcJd1qagcNfp06ctItxlCHfu3MGaNWvg6OgIPz8/1CtY7mIh1KQW/Vx1VYXU7NRi35kiqs7LeydkJOR/VnCx2NhjahMzM5VQlz5d+vIKz7GXIQoKdw0ePFircJe1ICJs27YNM2bMwL/+9S989dVXJqvIMQVZWVkICwvD3bt3MXToUDRo0MBqtlT4toJW524j2KByhcrFxNFc7V2xYsCKEkfVps57a2u0ytNrV7go8G6jd3HwzkGjq2LKYyMX7zwtRxgi3GUN4uLi4Ofnh3v37mH79u0W0XgxhOjoaISEhMDDwwP9+/e3yuJywdI/BzsHpOUU1wV2tHPU+rmpFhxNPaBCn2hYSZ1weVyA5SJg5QRDhbsszd69e9GyZUu8/fbbOH/+vKycem4uE+7avXs3+vfvj6FDh1rNqRfUnk/LSYOtYAsbgf3vaSvYYkqbKaJa6KZacJSa95aa/tBnV54uvbHpFL4AK458fgtzDIKIcPHiRZw4ccIg4S5LkZSUhGnTpuHMmTPYs2cPOnXqZG2TCvHo0SOEhISgZs2amDJlChyKDj2xINq05/PEtQpGngfvHNSrw14SpFTEGFJ/LtatW5C8/Y2pZ5eiS19e4RF7KSQlJQUqlQqXL1/G2LFj0aFDB1k59aNHj6JFixaoUqUKIiIiZOXU1Wo1wsPDoVKpkKJIwYyHM+C02MmqC29SI09zTwaSIgdsyKBoXTK/edgKtkYPnuaTksThEXspgohw7dq1fOGuLl26mH1ykCGkpaXhq6++wv79+7Fp0yb07t3b2iYVIj4+HsHBwXBwcEC1btUw/dh0WXQ+So08LSEOpq9j1pD0R0F7o5OjtQ66Lkl6ydpiaXKGL56WEgoKdw0bNqxEwl3m4PTp0/D19UWnTp2wYsUKVK1a1dom5UNEOHv2LP7880/07NkTnp6eaLCigWwW3kpTdUdJFiy1acPkOX1jjlce4Z2nZYiCwl3vvfee7MoE84S7fv75Z3jnzYiTCc+fP0doaCiICBMmTED16tUByGvhrTRFniXpTBX7NWCJTtfyhnw8BKcY5hbuKimRkZHw8fHB66+/joiICNSuXdvaJuVDRLh8+TKOHTuGTp06oWPHjoUaoeS28GYtmV5DMfVDqDQ91EoTPBUjU0qDcNeyZcuwdOlSWQp37d+/Hy9evIC3t7dWpcjSlP7gcPLgqZhSSk5ODo4dO4br16/LUrjr9u3b8PX1haOjIy5evAh3d3mVlhUU7ho5cqTo4jKPFDllGR6xy4iHDx8iODgYderUwbvvvisrjReNRoPVq1fj22+/xbx58zB16lRZabxkZGTg4MGDePz4Mby9va2i8cLhmBsesZci1Go1/vjjD1y6dAkDBgxA06ZNrW1SIWJiYjBu3Dikp6fj1KlTaNy4sbVNKsSdO3ewf/9+NGnSBJMmTYKdnZ21TeJwrAp37FamoHDXpEmTZCvc9cUXX2DmzJmyq8gJCwvDvXv38N5778HDw8PaJnE4skA+/5eWM+Qu3PX06VNMmjQJ9+7dw++//y4rjRegsHDX5MmTZbW4zOFYG+7YrUBiYiJCQkJgY2ODCRMmoFq1atY2qRB79+7F1KlTMXbsWAQFBcnKaebm5uLYsWO4du0aBg0ahDdNPZOOwykDcMduQfKEu44fP46uXbvKTuMlKSkJn332Gc6ePYu9e/fKSuMFkJdwl9wo2tVZUq1zTumGO3YLkZKSgn379iEjIwPjxo1DzZo1rW1SIY4ePYqPP/4YQ4YMQUREBBzNMfPNSNRqNf78809cuHAB/fr1Q7NmzWT1QLQ22hQXC85MtaYODsc68HJHM0NEuHr1ar5wV9euXblwlwHEx8cjJCQEDg4OGDJkiKwWl+WCvoEWeXD9ldIPL3eUAQWFu5RKpWyFuzp27IgrV67IUrjr5MmT8PLygqenZ6mJ0rWJXZkzUpaqb8MHUJQfuGM3E1y4y3gKCnd9/PHH+cJdpQFDBlGYCikDLfK2K2gn77otu8indbCMkJWVhdDQUBw+fBgjRoxA3759ZeXUIyIi0LZtW9y6dQuRkZGycupEhEuXLmHDhg1o1KgRfH19DXLqcphYb8ggClMhZaBFQcXEoqP48h4+Jblfcrj3nFfwHLsJKS3CXUuWLMGYMWNkldqQItylC7mIepl6ILRUDKmKMfUQaLnc+/KA1Bw7d+wmICcnB7///jtu3LghW+GuMWPGwMnJCZs2bZKdcNe1a9dw+PBheHp6olu3bkYtLstlYr1c7NCFqR8+peGaywpSHTtPxZSQhw8fYt26dUhPT8eUKVNk5dQ1Gg1WrVqFTp06YfTo0QgLC5OVU09PT8fu3bsRHh6OUaNGwcvLy+iKIX2DMyyVKtCWFrGzsUNqdqps0hRimvPGatHLaWgJh1Eixy4IwmJBEG4KgnBFEIRgQRDkU1ZhZtRqNY4fP44dO3bAy8sLw4cPl5UaY0xMDPr27QuVSoXTp0/j008/lZUa4507d7B27VqcyzyHxTmL4bbBrUROT5ezMkdOWYyiA6Fd7V0hCAISMhLMfm6pmHoItKkfFJySU9L/048CaEZELQDcBjC75CbJn7i4OGzYsAFPnjzBpEmTZKXGSETYunUrPD090atXL/z555+yUmPMysrC/v37ceDAAdi0tMHqmNWISYkpsdPT5awsvaCpbK5E1OdR0MzTwKmiE7LV2RY7txSKPnwULooS5cNN/aDglByT5dgFQfAGMIKI9P7XUVpz7HIX7oqLi4Ofnx/u3buH7du3y1q4q3///nhzzZsmX8TTVsJnrQVNwHqLqZaGl09aBms0KI0HEGTC48mK0iDc9cknn5Qq4S5T52bF5oYaMt/U1A5KbrNVzUVpmdlaXtDr2AVB+B3Aa1q+mkNEoS+3mQMgF4Dob2hBEPwA+AGQ1QKePkqTcNeePXtkKdwVHByMWrVqFRPuspTT8+/lr7Ucr2iqwBzNRVLPzeGYkhKnYgRB8AUwGUAvIkrXtz1QelIxecJd6enp8Pb2lp1wV1hYWL5w16JFi2Qp3HX+/Hn0799fq3CXJeufpUTi5irb42kKjqmwSB27IAj9AfwXQHciipe6n9wde2kS7tq4cSP69OljbZMKER8fj+Dg4HzhripVqohuKyenV17y4ZzSi6Vy7KsAVAJw9GU0dpaIJpfwmFYlPT0dBw4cQHx8PBfuMhBjhLvMlZs15oFRXvLhnLJPiRw7Eb1hKkPkQEHhLm9vb1lpvGRlZWH+/PnYsmULVq9ejffee8/aJhUiKSkJISEhshDuMjZXzvPhnLKCfDyXhbl5E1izBjh1CrCxyUL37odRq1Y0PvhgBBQKhbXNK0RkZCR8fHzQsGFDREZGGqyjYk6ICJcvX8axY8fQqVMndOzY0eqNULrq1nU59rzv5JIa4nCMpdxpxRABCxYwpz5xItCx4338/XcosrMbYsuWvujRoxLWrgXkEKwXFO5aunQpfHx8ZFWRU1LhLnPBc+WcsgoftCHC6tXAnj3A5cs5uHLlGK5fvw5vbybc9fnnwNChwKxZwNKl1rXz9u3b8PX1haOjIy5evCi7EtG///4bhw4dgqenJ0aOHCmrxWWeK+eUd+QjHmIBsrMBf39g9eqH2Lt3HaodOoTPly9HozffBDw84BSqQlAQsGkT8OyZdWzUaDRYuXIlOnXqhI8++kh2wl0ZGRnYs2cPTpw4UWLhLnPBW9w55Z1yFbEfOqRGv37/w7lzFzFSrYbbhg1A+stcbHQ04OeHGgHAkCFK/PILMG2aZe178OABxo0bh7S0NJw+fVpWGi8AE+7av38/mjRpgkmTJsHOzs7aJmmF58o55Z1y49jj4uJw9Wow6tZ1xqRJk+DcvPkrp55HejowZw6aTlUixoKKo0SEbdu2YebMmfjXv/6FmTNnyq4iJywsDHfv3oW3tzcaNGhgbZP0wlvcOeUZ+XgPM1FQuKtq1V64evUdODsLEPXcMTFITAScnCxjX1xcHCZNmoS7d+/i6NGjshTuCg0NhUKhwJQpU2SlQcPhcLRTph17YmIiQkNDIQgCJkyYgKysavj3v4GkJKCquztLvxSB3Nzxyy9ASIj57QsODsbUqVMxduxY7Ny5U1ZOU0y4i8PhyJ8y6djzhLtOnDiBLl26FBLuGjIE+P/27j826vqO4/jzhbiAczMkFucErTNmTpwITkJb/1hatpShkBKXaAopbqaVzOASzDYlk7gFs2AUExB0YQtNROYitB2SgXSooOLscGAx1UUREcZsFimUVDuOvvfH3ZkT+4MrvX4+d30/EhK+pfReKdy7n/t8v/f6PvAAPLFsGaqt/eJ2zAUX8HzJMq44AlOn5i5fR0cHixYtYvfu3dEWdzU2NlJUVPSl4i7nXPwKbrCni7s+/fRTFixY8KXirpUroaIC7vikmuW/hsufXAKHDnHq0stZP2kZv3mjmpdfzl2+7du3f17ctXfv3rwr7nLOxa9gBvvZFndddBG89BI89hiUrqpm9OhqRn8Ljh+HBZPh9achF++zyafirrq6un6Lu5xzcSuIwZ5tcdeFF8KDDya3ZD78EHp6YOJEGDMmN/nSxV2lpaXRFnft2rWL8vLysyrucs7FLe8H+7kUd40eDVddlbts3d3dLF26lPr6elavXk1VVVXuHmwQjh07RlNTEz09Pdx1111Bi7ucc0Mnbwd7d3c3W7du5eDBg9x2mxd3ZSPG4i7n3NDJy2fzBx98wJo1axg1ahR33313VEM9kUjw8MMPM2PGDBYvXsymTZuiGuqdnZ1s2LCBlpYWampqKCsrG1FDfX3reoofL2bUQ6MofryY9a193s3RubyVVyv2U6dO0dzcTFtbG7femizuiokXd8UtF/c0dS5GebNUO3LkCE899RRdXV0sXLgwqqHe09PDqlWrKCsrY968eV7cFan+etqdKyTRr9hPnz7Nzp072bNnDzNnzmTSpEmhI31Burjr5MmTvPrqq17cFbFDx3uvkejr487lq6hX7O3t7axdu5ajR49SV1cX1VA3M+rr65k6dSrl5eW88sorUQ317u5uNm/ezJYtW6iqqqKysnJED3Xou4/de9pdoYlyxZ5Z3FVRUcGUKVOiura6vb2d2tpaDhw4QHNzc5TFXY2NjRQXF3txVwa/p6kbKaIb7GcWd40bNy50pC/ILO569tlnoxqaiUSCHTt20Nra6sVdvfCedjdSRHPP0/6Ku2KQWdxVX18fdXHXrFmzvLjLuQKUV/c8Hai4KzQv7nLO5ZOgg93M2L9/P1u3bmXatGncfPPNUV2Glw/FXY2NjYwdO9aLu5xznws22LMt7hpu6eKukpISL+5yzuWVIIP9s88+Y82aNYMq7sq12Iu7Ojo6aGxs9OIu51yfgkzUEydOeHFXljKLu8rKypg+ffqI6nhxzp29IIN9/PjxUQ31RCLB8uXLWbFiBY8++ijz58+Pamujs7OTzZs309nZSU1NTVQ/cJxz8YlnDyQQL+5yzhWaEftavqenh5UrV1JaWkp1dXV0xV1dXV1e3OWcG5QRuWI/dOgQd955J11dXbz22mtRdbyAF3c5587NiFqxp4u7brzxRioqKti1a1dUQ92Lu5xzQ2HErNjb29upq6vj/fff9+Iu51xBGxEr9oaGBiZPnsw111xDS0tLVEM9kUiwbds2nnvuOSorK5kzZ44PdefcOSnoFXtmcdfGjRujLO5qaGigqKiIhQsXenGXc25IFOxg9+Iu59xINSSDXdJ9wCNAkZn9dyi+5mB5cZdzbqQ758EuaSLwAyD4jSO9uMs554Zmxb4C+AXQNARfa1DSxV3r1q1j9erVzJ07N1SUXh07doympiYv7nLODYtzGuySZgNHzGxfyNXnokWL+Pjjj9m3bx+XXHJJsBy9SSQS1NfXc9NNN1FSUuLFXc65nBvw1niSmoFv9PJHS4AHgB+a2XFJB4Hv9bXHLqkWqE0dXgfsH2zoHLoYCHqOoA+eKzux5oJ4s3mu7ITKdYWZDXiLuUHf81TSd4G/Aelbvk8A/g1MM7P/DPB3/3E29+0bbp4rO54re7Fm81zZiTVX2qC3YsysFfi8P3agFbtzzrnh4Ru+zjlXYIbsDUpmVpzFp/9+qB53iHmu7Hiu7MWazXNlJ9ZcwDnssTvnnIuTb8U451yBCT7YJd0nySRdHDoLgKTfSnpL0l5JL0j6ZuhMAJIekfROKluDpCjeVivpx5LeltQjKfhVApIqJb0r6T1JvwqdB0DSHyW1S4rqEl9JEyW9KKkt9W94b+hMAJLGSHpD0r5UrodCZ8ok6TxJ/5T0fOgsfQk62GOqI8jwiJldb2Y3AM8DD4YOlLIduM7Mrgf+BdwfOE/afmAusDN0EEnnAU8AM4FrgTskXRs2FQDrgMrQIXqRABab2XeA6cDPIvl+dQPlZjYZuAGolDQ9cKZM9wJtoUP0J/SKPV1HEM1Gv5mdyDj8KpFkM7MXzCyROnyd5PsGgjOzNjN7N3SOlGnAe2Z2wMz+B/wJmBM4E2a2E/gkdI4zmdlRM3sz9ftOksPqsrCpwJJOpg7PT/2K4nkoaQIwC1gbOkt/gg32zDqCUBn6ImmZpI+AauJZsWf6CfDX0CEidBnwUcbxYSIYVPlAUjEwBfh72CRJqe2OvUA7sN3MosgFPE5yMdoTOkh/ctrHfjZ1BLl8/L70l8vMmsxsCbBE0v3APcDSGHKlPmcJyZfQ64cj09nmikRvhUVRrPRiJulCYCPw8zNesQZjZqeBG1LnkhokXWdmQc9RSLoFaDezPZK+HzLLQHI62M1sRm8fT9URXAmky8MmAG9KGrCOIJe5evEMsIVhGuwD5ZJUA9wCVNgwXqeaxfcrtMPAxIzjdM2F64Ok80kO9fVmtil0njOZWYekl0ieowh98rkMmC3pR8AY4OuSnjazeYFzfUmQrRgzazWz8WZWnHpj02Fg6nAM9YFIujrjcDbwTqgsmSRVAr8EZptZ10CfP0K1AFdLulLSV4Dbgb8EzhQtJVdVfwDazOyx0HnSJBWlr/qSNBaYQQTPQzO738wmpGbW7cCOGIc6hD95GqPfSdov6S2SW0VRXAIGrAK+BmxPXYr5ZOhAAJKqJB0GSoAtkraFypI6uXwPsI3kicA/m9nbofKkSdoA7Aa+LemwpJ+GzpRSBswHylP/p/amVqOhXQq8mHoOtpDcY4/20sIY+TtPnXOuwPiK3TnnCowPduecKzA+2J1zrsD4YHfOuQLjg9055wqMD3bnnCswPtidc67A+GB3zrkC838XKMWQipldoAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_classifier(X1_train, X2_train, clf):\n",
    "    plt.plot(X1_train[:,0], X1_train[:,1], \"ro\")\n",
    "    plt.plot(X2_train[:,0], X2_train[:,1], \"go\")\n",
    "    plt.scatter(soft_margin_svm.spv[:,0], soft_margin_svm.spv[:,1], \n",
    "                s=100, c=\"\", edgecolors=\"b\", label=\"support vector\")\n",
    "\n",
    "    X1, X2 = np.meshgrid(np.linspace(-4,4,50), np.linspace(-4,4,50))\n",
    "    X = np.array([[x1, x2] for x1, x2 in zip(np.ravel(X1), np.ravel(X2))])\n",
    "    Z = soft_margin_svm.project(X).reshape(X1.shape)\n",
    "    plt.contour(X1, X2, Z, [0.0], colors='k', linewidths=1, origin='lower')\n",
    "    plt.contour(X1, X2, Z + 1, [0.0], colors='grey', linewidths=1, origin='lower')\n",
    "    plt.contour(X1, X2, Z - 1, [0.0], colors='grey', linewidths=1, origin='lower')\n",
    "    plt.legend()\n",
    "    plt.show()\n",
    "    \n",
    "plot_classifier(X_train[y_train==1], X_train[y_train==-1], soft_margin_svm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy of soft margin svm based on sklearn:  0.925\n"
     ]
    }
   ],
   "source": [
    "from sklearn import svm\n",
    "# 创建svm模型实例\n",
    "clf = svm.SVC(kernel='linear')\n",
    "# 模型拟合\n",
    "clf.fit(X_train, y_train)\n",
    "# 模型预测\n",
    "y_pred = clf.predict(X_test)\n",
    "# 计算测试集准确率\n",
    "print('Accuracy of soft margin svm based on sklearn: ', \n",
    "      accuracy_score(y_test, y_pred))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
